﻿//-----------------------------------------------------------------------
// <copyright file="AuthorizationStoreManager.cs" company="Alan Rodrigues">
//     Copyright (c) Alan Rodrigues. All rights reserved.
// </copyright>
// <developed>Alan Rodrigues (operninha@gmail.com)</developed>
//-----------------------------------------------------------------------

namespace Authorization
{
    //TODO: OTIMIZAR ou CRIAR CACHE
    //MERGE, REJECTS, PRIORITY APPS CONFLICTS IN STORE
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Xml.Linq;

    ///<summary>
    ///AuthorizationContext Store Manager
    ///</summary>
    public static class AuthorizationStoreManager
    {
        private const string _connectionStringProviderName = "Authorization";
        private static readonly List<AuthorizationStore> storeManagerCollection = new List<AuthorizationStore>();
        //private static readonly ApplicationCollection applications = new ApplicationCollection();
        //rivate static readonly Dictionary<string, AuthorizationStore> applicationToStoreLink = new Dictionary<string, AuthorizationStore>();
        //private static readonly ApplicationCollection applications = new ApplicationCollection();
        private static readonly Dictionary<string, Application> idxApps = new Dictionary<string, Application>();
        
      static AuthorizationStoreManager()
        {
            foreach (ConnectionStringSettings conn in ConfigurationManager.ConnectionStrings)
            {
                if (conn.ProviderName.Equals(_connectionStringProviderName, StringComparison.OrdinalIgnoreCase))
                {
                    var manager = new AuthorizationStore(conn);
                    manager.OnStoreInitialized += new EventHandler(manager_OnStoreInitialized);
                    manager.OnStoreUpdated += Manager_OnStoreUpdated;
                    manager.Initialize();
                    storeManagerCollection.Add(manager); 
                }
            }
        }

        static void manager_OnStoreInitialized(object sender, EventArgs e)
        {
            AuthorizationStore manager = sender as AuthorizationStore;

            lock (idxApps)
            {
                if (manager.StoreState == StoreState.OK)
                {

                    ApplicationCollection apps = manager.GetApplications();

                    for (int i = 0; i < apps.Count; i++)
                    {
                        var app = apps[i];

                        if (!idxApps.ContainsKey(app.Name))
                            idxApps.Add(app.Name, app);

                    }
                }
            }

        }

        private static void Manager_OnStoreUpdated(object sender, EventArgs e)
        {
            UpdateApplicationIndex(sender);
        }

        private static void UpdateApplicationIndex(object sender)
        {
            lock (idxApps)
            {
                var store = (AuthorizationStore)sender;


                //var appsLinked = from a in applicationToStoreLink
                //               where a.Value == store
                //             select a.Key;

                var appsLinked = from a in idxApps.Values
                                 where a.AuthorizationStore == store
                                 select a.Name;

                var appsLinkedList = appsLinked.ToList();

                ApplicationCollection appsInStore = store.GetApplications();

                foreach (var app in appsInStore) // Applications already in store? No? Add, Yes? Do Nothing
                {
                    if (appsLinkedList.Contains(app.Name))
                    {
                        appsLinkedList.Remove(app.Name);
                    }
                    else
                    {
                        if (!idxApps.ContainsKey(app.Name))
                            idxApps.Add(app.Name, app);
                    }
                }

                //Any Application removed from Store? Yes? Remove from list, no? Do Nothing

                foreach (string appName in appsLinkedList)
                {
                    idxApps.Remove(appName);
                }
            }
        }

        public static string[] GetApplicationNames()
        {
            return idxApps.Keys.ToArray();
            //var apps = new List<string>();

            //foreach (var manager in storeManagerCollection)
            //{
            //    var managerApps = manager.GetApplications();

            //    foreach (Application application in managerApps)
            //    {
            //        apps.Add(application.Name);
            //    }
            //}

            //return apps.ToArray();
        }
        public static ApplicationCollection GetApplications()
        {
            return new ApplicationCollection(idxApps.Values.ToList());    

            //var apps = new ApplicationCollection();

            //foreach (var manager in storeManagerCollection)
            //{
            //    var managerApps = manager.GetApplications();

            //    foreach (Application application in managerApps)
            //    {
            //        apps.Add(application);
            //    }
            //}

           // return apps;
        }

        public static IEnumerable<Application> ApplicationsEnumerator
        {
            get
            {
                foreach (var manager in storeManagerCollection)
                {
                    foreach (var application in manager.GetApplications())
                    {
                        yield return application;
                    }
                }
            }
        }
        
/*
        private static void Manager_OnStoreUpdated(object sender, EventArgs e)
        {
            //var apps = from a in applications
            //           where a.AuthorizationStore == obj
            //           &&  !obj.GetApplications().Contains(
            //           select a;
       

            //obj.GetApplications();
        }
*/


        public static bool ApplicationExists(string applicationName)
        {
            return (GetApplication(applicationName) != null);
        }

        public static Application CreateApplication(string storeName, string applicationName, string version, string description, string applicationData)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");

            if (ApplicationExists(applicationName))
                throw new AuthorizationException(SR.GetString("application_already_exists", applicationName));

            AuthorizationStore store = storeName == null ? GetDefaultStore() : GetStoreByName(storeName);

            var app = store.CreateApplication(applicationName, version, description, applicationData);

            lock (idxApps)
            {
                idxApps.Add(applicationName, app);
            }

            return app;
        }

        private static AuthorizationStore GetDefaultStore()
        {
            return storeManagerCollection[0];
        }

        public static string GetDefaultStoreName()
        {           
            if (storeManagerCollection.Count > 0)
                return storeManagerCollection[0].Name;
            else
                return null;
        }

         public static AuthorizationStore DeleteApplication(string applicationName)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");

            var app = GetApplication(applicationName);
            var store = app.AuthorizationStore;
            store.DeleteApplication(applicationName);
            lock (idxApps)
            {
                idxApps.Remove(applicationName);
            }
         
            return store;
        }

        public static Application UpdateApplication(string applicationName, string newApplicationName, string newVersion, string newDescription, string newApplicationData)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 64, "applicationName");

            if (ApplicationExists(applicationName))
                throw new AuthorizationException(SR.GetString("application_already_exists", applicationName));
          
            var app = GetApplication(applicationName);
            var store = app.AuthorizationStore;
           return store.UpdateApplication(applicationName, newApplicationName, newVersion, newDescription, newApplicationData);
        }

        ///<summary>
        /// Get a <see cref="Application"/> with specified name.
        ///</summary>
        ///<param name="name">The name of the <see cref="Application"/></param>
        ///<returns></returns>
        ///<exception cref="ArgumentNullException"></exception>
        public static Application GetApplication(string name)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");
            Application result;
            return idxApps.TryGetValue(name, out result) ? result : null;

            //return applications.SingleOrDefault(a => a.Name == name);

            
            //if (storeManagerCollection != null)
            //{
                //AuthorizationStore store = applicationToStoreLink[name];
                //if (store != null)
                //    return store.GetApplication(name);

                //foreach (AuthorizationStore manager in storeManagerCollection)
                //{
                //    Application app = manager.GetApplication(name);
                //    if (app != null)
                //    {
                //        return app;
                //    }
                //}
          //  }

          //  return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="storeName">The name of the store</param>
        /// <exception cref="AuthorizationException"></exception>
        public static void UpdateCache(string storeName)
        {
            SecUtility.CheckParameter(ref storeName, true, true, false, 0, "storeName");  
            AuthorizationStore store = storeManagerCollection.SingleOrDefault(s => s.Name == storeName);

            if (store != null)
            {
                store.UpdateCache(null);
            }
            else
            {
                throw new AuthorizationException(SR.GetString("store_name_not_found", storeName));
            }
        }

        /// <summary>
        /// Updates cache in all Store managed by AuthorizationStoreManager
        /// </summary>
        /// <example>using Authorization;
        ///         public void UpdateStore()
        ///         { 
        ///             <see cref="AuthorizationStoreManager"/>.UpdateCache();
        ///         }
        /// </example>
        public static void UpdateCache()
        {
            for (int i = 0; i < storeManagerCollection.Count; i++)
            {
                var store = storeManagerCollection[i];
                store.UpdateCache(null);
            }
        }

        public static string ExportApplication(Application app, bool isToMerge, bool isCopyWithAdministrators, bool isCopyWithUsers)
        {
            System.Collections.ArrayList listApp = new System.Collections.ArrayList();
            listApp.Add(app.Name);

            string tempFileOld = System.IO.Path.GetTempFileName();
            string tempFile = System.IO.Path.ChangeExtension(tempFileOld, "xml");            
            string destUrl = string.Format("msxml://{0}", tempFile);

            AuthorizationContextHelper.AzManTransportHelper.CopyApplications(app.AuthorizationStore.Store, destUrl, listApp, isToMerge, isCopyWithAdministrators, isCopyWithUsers);

            return Convert.ToBase64String(System.IO.File.ReadAllBytes(tempFile),Base64FormattingOptions.InsertLineBreaks); 
        }


        public static string ImportApplication(string xmlData, string applicationName, AuthorizationStore store, bool isToMerge)
        {
            string tempFile = System.IO.Path.GetTempFileName();                                               
        
            System.Collections.ArrayList appList = new System.Collections.ArrayList();
            appList.Add(applicationName);

            byte[] byteArray = Convert.FromBase64String(xmlData);
            System.IO.File.WriteAllBytes(tempFile, byteArray);                       

            string sourceUrl = string.Format("msxml://{0}", tempFile);
            //sourceUrl += ".xml";            

            AuthorizationContextHelper.AzManTransportHelper.CopyApplications(sourceUrl, store, appList, isToMerge);
            
            return "";
        }
        
        public static AuthorizationStore GetStoreByName(string name)
        {
            foreach (var store in storeManagerCollection)
            {
                if (store.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                    return store;
            }

            return null;
        }

        public static AuthorizationStore[] GetAllStores()
        {
            return storeManagerCollection.ToArray();
        }

        public static string[] GetAllStoreNames()
        {
            IEnumerable<string> i = from G in storeManagerCollection
                         select G.Name;
            return i.ToArray<string>();
            //return  (string[])  from o in storeManagerCollection
            //                    select o.Name;

        }


    }
}
